

package top.continew.admin.question.manger.service.impl;

import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import top.continew.admin.common.context.UserContextHolder;
import top.continew.admin.question.manger.base.PageFactory;
import top.continew.admin.question.manger.service.model.*;
import top.continew.admin.question.mapper.SamplePoolMapper;
import top.continew.admin.question.mapper.question.QuestionMapper;
import top.continew.admin.question.mapper.test.TestMapper;
import top.continew.admin.question.model.MaxBatchNo;
import top.continew.admin.question.model.SampleBatch;
import top.continew.admin.question.model.SamplePool;
import top.continew.admin.question.model.SampleSourceTest;
import top.continew.admin.question.model.question.Question;
import top.continew.admin.question.model.test.Test;
import top.continew.admin.question.manger.service.SpotCheckService;
import top.continew.admin.question.manger.service.msg.MessageBean;
import top.continew.admin.question.manger.service.msg.MessageTopic;
import top.continew.admin.question.manger.service.msg.MessageType;
import top.continew.starter.core.validation.ValidationUtils;

import java.util.*;
import java.util.stream.Collectors;

@Transactional(rollbackFor = Exception.class)
@Service

public class SpotCheckServiceImpl extends ServiceImpl<SamplePoolMapper, SamplePool> implements SpotCheckService {
    @Autowired
    private QuestionMapper questionMapper;
    @Autowired
    private TestMapper testMapper;
    @Autowired
    private SamplePoolMapper samplePoolMapper;

    @Override
    public void addSpotCheck(SpotCheck spotCheck) {

        ValidationUtils.throwIf(spotCheck.getSum() > 0, "样本量需要大于0");
        ValidationUtils.throwIf(spotCheck.getSum() <= 1000, "样本总量最大为1000");
        ValidationUtils.throwIf(spotCheck.getSampleSum() >= 10, "样本量需要大于10");
        ValidationUtils.throwIf(spotCheck.getSampleSum() < spotCheck.getSum(), "样本量不能大于总量");
        // 1根据样本量和查询条件查询题目id
        List<SpotCheckQuestion> spotCheckQuestions = questionMapper.querySampleQuestion(spotCheck);
        ValidationUtils.throwIf(!spotCheckQuestions.isEmpty(), "样本量为空~");
        // 3,设置抽样库
        SampleBatch sampleBatch = createSampleBatch("1", spotCheck.getSubjectId(), spotCheck.getSampleSum(), spotCheck
            .getSum(), spotCheck.getSubjectiveObjectiveRatio());
        // 抽题
        drawQuestions(spotCheckQuestions, sampleBatch);
    }

    private SampleBatch createSampleBatch(String source,
                                          Long subjectId,
                                          Long sampleSum,
                                          Long sum,
                                          Double subjectiveObjectiveRatio) {
        // 获取最大的批次号
        MaxBatchNo maxBatchNo = new MaxBatchNo().selectOne(new QueryWrapper<MaxBatchNo>().eq("type", source));
        if (!maxBatchNo.getDate().equals(DateUtil.format(DateUtil.beginOfDay(new Date()), "yyyy-MM-dd"))) {
            maxBatchNo.setMaxBatchNo(1L);
            maxBatchNo.setDate(DateUtil.format(DateUtil.beginOfDay(new Date()), "yyyy-MM-dd"));
        } else {
            maxBatchNo.setMaxBatchNo(maxBatchNo.getMaxBatchNo() + 1);
        }
        maxBatchNo.updateById();
        SampleBatch sampleBatch = new SampleBatch();
        sampleBatch.setSubjectId(subjectId);
        sampleBatch.setBatchNo(("1".equals(source) ? "Q" : "T") + DateUtil.format(DateUtil
            .beginOfDay(new Date()), "yyyy-MM-dd") + String.format("%04d", maxBatchNo.getMaxBatchNo()));
        sampleBatch.setSampleSum(sampleSum);
        sampleBatch.setSampleSource(source);
        sampleBatch.setSamplePoolSum(sum);
        sampleBatch.setStatus("0");
        sampleBatch.setSubjectiveObjectiveRatio(subjectiveObjectiveRatio);
        sampleBatch.setCreateBy(UserContextHolder.getUserId());
        sampleBatch.setCtime(System.currentTimeMillis());
        sampleBatch.setUtime(System.currentTimeMillis());
        sampleBatch.insert();
        return sampleBatch;
    }

    /**
     * 抽样
     */
    private void drawQuestions(List<SpotCheckQuestion> spotCheckQuestions, SampleBatch sampleBatch) {
        // 2,修改题目状态为抽检中
        questionMapper.changeQualified(spotCheckQuestions.stream()
            .map(SpotCheckQuestion::getId)
            .collect(Collectors.toList()), "1");
        List<SamplePool> samplePools = new ArrayList<>(Math.toIntExact(sampleBatch.getSampleSum()));
        // 4,根据样本数量和主客观比,从样本中随机获取样本  主:客=0.6:1
        int subjectiveSum;
        if (sampleBatch.getSubjectiveObjectiveRatio() == 1.00) {
            subjectiveSum = Math.toIntExact(sampleBatch.getSampleSum());
        } else if (sampleBatch.getSubjectiveObjectiveRatio() == 0.00) {
            subjectiveSum = 0;
        } else {
            subjectiveSum = (int)(sampleBatch.getSampleSum() * sampleBatch.getSubjectiveObjectiveRatio() / (sampleBatch
                .getSubjectiveObjectiveRatio() + 1));
        }

        // 主观题
        List<SpotCheckQuestion> subjectiveQuestions = spotCheckQuestions.stream()
            .filter(res -> Arrays.stream(new Long[] {4L, 5L, 6L, 8L, 9L, 10L}).anyMatch(a -> a.equals(res.getType())))
            .collect(Collectors.toList());
        ValidationUtils.throwIf(subjectiveQuestions.size() >= subjectiveSum, "主观题题量不足");
        int[] subjectiveIndexs = randIndex(subjectiveQuestions.size(), subjectiveSum);
        for (int i = 0; i < subjectiveIndexs.length; i++) {
            SpotCheckQuestion spotCheckQuestion = subjectiveQuestions.get(i);
            SamplePool samplePool = new SamplePool();
            samplePool.setBatchNo(sampleBatch.getBatchNo());
            samplePool.setIsSample(String.valueOf(subjectiveIndexs[i]));
            samplePool.setCreateBy(UserContextHolder.getUserId());
            samplePool.setStatus("0");
            samplePool.setTestId(spotCheckQuestion.getTestId());
            samplePool.setQuestionId(spotCheckQuestion.getId());
            samplePool.setCtime(System.currentTimeMillis());
            samplePool.setUtime(System.currentTimeMillis());
            samplePools.add(samplePool);
        }

        // 客观题
        int objectiveSum = Math.toIntExact(sampleBatch.getSampleSum() - subjectiveSum);
        List<SpotCheckQuestion> objectiveQuestions = spotCheckQuestions.stream()
            .filter(res -> Arrays.stream(new Long[] {1L, 2L, 3L, 7L}).anyMatch(a -> a.equals(res.getType())))
            .collect(Collectors.toList());
        ValidationUtils.throwIf(objectiveQuestions.size() >= objectiveSum, "客观题题量不足");
        int[] objectiveIndexs = randIndex(objectiveQuestions.size(), objectiveSum);
        for (int i = 0; i < objectiveIndexs.length; i++) {
            SpotCheckQuestion spotCheckQuestion = objectiveQuestions.get(i);
            SamplePool samplePool = new SamplePool();
            samplePool.setBatchNo(sampleBatch.getBatchNo());
            samplePool.setIsSample(String.valueOf(objectiveIndexs[i]));
            samplePool.setCreateBy(UserContextHolder.getUserId());
            samplePool.setStatus("0");
            samplePool.setTestId(spotCheckQuestion.getTestId());
            samplePool.setQuestionId(spotCheckQuestion.getId());
            samplePool.setCtime(System.currentTimeMillis());
            samplePool.setUtime(System.currentTimeMillis());
            samplePools.add(samplePool);
        }
        this.saveBatch(samplePools, samplePools.size());
    }

    /**
     * 随机产生下标
     */
    private int[] randIndex(int size, int sampleSum) {
        int[] indexs = new int[size];
        if (size == 0 || sampleSum == 0) {
            return indexs;
        }
        if (size == 1) {
            indexs[0] = 1;
            return indexs;
        }
        Random random = new Random();
        while (sampleSum > 0) {
            int index = random.nextInt(size);
            if (indexs[index] == 0) {
                indexs[index] = 1;
                sampleSum--;
            }
        }
        return indexs;
    }

    @Override
    public void addTestSpotCheck(TestSpotCheck testSpotCheck) {
        ValidationUtils.throwIf(testSpotCheck.getSum() > 0, "样本量需要大于0");
        ValidationUtils.throwIf(testSpotCheck.getSum() <= 1000, "样本总量最大为1000");
        ValidationUtils.throwIf(testSpotCheck.getSampleSum() >= 10, "样本量需要大于10");
        ValidationUtils.throwIf(testSpotCheck.getSampleSum() <= testSpotCheck.getSum(), "样本量不能大于总量");
        // 创建批次
        SampleBatch sampleBatch = createSampleBatch("2", -1L, testSpotCheck.getSampleSum(), testSpotCheck
            .getSum(), testSpotCheck.getSubjectiveObjectiveRatio());
        // 获取试卷id
        QuerySpotCheckTest querySpotCheckTest = new QuerySpotCheckTest();
        BeanUtils.copyProperties(testSpotCheck, querySpotCheckTest);
        List<SpotCheckTestVO> spotCheckTestList = testMapper.getSpotCheckTestList(querySpotCheckTest);
        // 添加试卷到样本池
        List<Long> testIds = spotCheckTestList.stream().map(res -> {
            SampleSourceTest sampleSourceTest = new SampleSourceTest();
            sampleSourceTest.setTestId(res.getId());
            sampleSourceTest.setCtime(System.currentTimeMillis());
            sampleSourceTest.setUtime(System.currentTimeMillis());
            sampleSourceTest.setCreateBy(UserContextHolder.getUserId());
            sampleSourceTest.setBatchId(sampleBatch.getId());
            sampleSourceTest.insert();
            // 设置试卷为抽检中
            Test test = new Test().selectById(res.getId());
            test.setQualifiedStatus("1");
            test.updateById();
            return res.getId();
        }).collect(Collectors.toList());
        // 获取试卷下所有的题目
        List<SpotCheckQuestion> spotCheckQuestions = testMapper.querySampleQuestion(testIds);
        // 进行抽样
        drawQuestions(spotCheckQuestions, sampleBatch);
    }

    @Override
    public void addSpotCheckAgain(String batchNo) {
        // 判断是否所有题目都已经审核通过
        Integer noReviewPassSum = samplePoolMapper.checkQuestionReviewPass(batchNo);
        ValidationUtils.throwIf(noReviewPassSum == null || noReviewPassSum == 0, "题目尚未审核完成");
        // 修改原抽检状态为2次抽检
        SampleBatch oneSampleBatch = new SampleBatch().selectOne(new QueryWrapper<SampleBatch>()
            .eq("batch_no", batchNo));
        oneSampleBatch.setStatus("3");
        oneSampleBatch.updateById();
        // 获取最大批次号
        SampleBatch sampleBatch = createSampleBatch(oneSampleBatch.getSampleSource(), oneSampleBatch
            .getSubjectId(), oneSampleBatch.getSampleSum(), oneSampleBatch.getSamplePoolSum(), oneSampleBatch
                .getSubjectiveObjectiveRatio());
        if ("2".equals(oneSampleBatch.getSampleSource())) {
            List<SampleSourceTest> sampleSourceTests = new SampleSourceTest()
                .selectList(new QueryWrapper<SampleSourceTest>().eq("batch_id", oneSampleBatch.getId()));
            // 复制试卷
            for (SampleSourceTest sampleSourceTest : sampleSourceTests) {
                sampleSourceTest.setId(null);
                sampleSourceTest.setBatchId(sampleBatch.getId());
                sampleSourceTest.insert();
                Test test = new Test().selectById(sampleSourceTest.getTestId());
                test.setQualifiedStatus("1");
                test.updateById();
            }
        }
        // 获取原抽检下所有的题目
        List<SpotCheckQuestion> spotCheckQuestions = samplePoolMapper.getSamplePoolByBatchNo(batchNo);
        // 抽题
        drawQuestions(spotCheckQuestions, sampleBatch);
    }

    @Override
    public List<SamplePoolVo> getSamplePool(SamplePoolQuery samplePoolQuery) {
        return PageFactory.page(() -> samplePoolMapper.getSamplePool(samplePoolQuery, UserContextHolder.getUserId()));
    }

    @Override
    public void checking(Long id, String status) {
        SamplePool samplePool = new SamplePool().selectById(id);
        ValidationUtils.throwIfEmpty(samplePool, "题目不存在!");
        samplePool.setStatus(status);
        samplePool.setUtime(System.currentTimeMillis());
        samplePool.setRummager(UserContextHolder.getUserId());
        samplePool.setRummagerTime(System.currentTimeMillis());
        samplePool.updateById();
    }

    @Override
    public UpAndDown queryUpAndDown(String batchNo, Long id) {
        SamplePoolVo queryUp = samplePoolMapper.queryUp(batchNo, id);
        SamplePoolVo queryDown = samplePoolMapper.queryDown(batchNo, id);
        UpAndDown upAndDown = new UpAndDown();
        upAndDown.setUp(queryUp);
        upAndDown.setDown(queryDown);
        return upAndDown;
    }

    @Override
    public List<BatchList> queryBatch(BatchQuery batchQuery) {
        return PageFactory.page(() -> samplePoolMapper.queryBatch(batchQuery));
    }

    @Override
    public void reject(String batchNo, String msg) throws Exception {
        checkStatusComplete(batchNo);
        // 设置抽样为不合格
        SampleBatch sampleBatch = new SampleBatch().selectOne(new QueryWrapper<SampleBatch>().eq("batch_no", batchNo)
            .last("limit 1"));
        sampleBatch.setStatus("2");
        sampleBatch.setRummager(UserContextHolder.getUserId());
        sampleBatch.setUtime(System.currentTimeMillis());
        sampleBatch.updateById();
        if ("2".equals(sampleBatch.getSampleSource())) {
            samplePoolMapper.rejectTest(sampleBatch.getId());
        }
        // 设置样本内题目为对应状态,不合格为驳回,设置样本外库题目为不合格,并状态驳回
        samplePoolMapper.changeSample(batchNo, "2");
        // 获取驳回题目的编辑人
        List<Question> questions = samplePoolMapper.getRejectPeople(batchNo);
        for (Question question : questions) {
            // 发送驳回消息
            MessageBean.builder()
                .topic(MessageTopic.SUBJECT_REVIEW_REJECTION)
                .message("您好,您的题目抽检合格率过低已被驳回,驳回理由:" + (msg != null && !"".equals(msg) ? msg : "无"))
                .type(MessageType.ERROR)
                .url("-")
                .build()
                .addCustomer(question.getEditPeople(), 2)
                .send();
        }
    }

    @Override
    public void pass(String batchNo) throws Exception {
        checkStatusComplete(batchNo);
        // 设置抽样为合格
        SampleBatch sampleBatch = new SampleBatch().selectOne(new QueryWrapper<SampleBatch>().eq("batch_no", batchNo)
            .last("limit 1"));
        sampleBatch.setStatus("1");
        sampleBatch.setRummager(UserContextHolder.getUserId());
        sampleBatch.setUtime(System.currentTimeMillis());
        sampleBatch.updateById();
        if ("2".equals(sampleBatch.getSampleSource())) {
            // 把不合格题目对应的试卷设置为驳回,抽检状态为未抽检
            samplePoolMapper.passTest(sampleBatch.getId());
        }
        // 设置样本内题目为对应状态,不合格为驳回,设置样本外库题目为不合格,并状态驳回
        samplePoolMapper.changeSample(batchNo, "3");
    }

    /**
     * 判断样本是否已经抽检完成
     */
    void checkStatusComplete(String batchNo) throws Exception {
        Long sum = samplePoolMapper.getNotCheckCount(batchNo);
        ValidationUtils.throwIf(sum == null || sum == 0L, "样本中存在未抽检的题目~");
    }

    @Override
    public List<TestSpotCheckVO> getTestSpotCheck(QueryTestSpotCheck queryTestSpotCheck) {
        return PageFactory.page(() -> samplePoolMapper.getTestSpotCheck(queryTestSpotCheck));
    }

    @Override
    public void revoke(String batchNo) {
        // 1.是否是抽检中
        SampleBatch sampleBatch = new SampleBatch().selectOne(new QueryWrapper<SampleBatch>().eq("batch_no", batchNo)
            .last("limit 1"));
        ValidationUtils.throwIfEmpty(sampleBatch, "样本不存在");
        ValidationUtils.throwIf("0".equals(sampleBatch.getStatus()), "已抽检不允许撤销~");
        if ("2".equals(sampleBatch.getSampleSource())) {
            // 把对应的试卷设置为未抽检
            samplePoolMapper.revokeTest(sampleBatch.getId());
        }
        // 2.设置题目为未抽检
        samplePoolMapper.revokeQuestion(sampleBatch.getBatchNo());
        // 3.设置样本库为已撤销
        sampleBatch.setStatus("4");
        sampleBatch.updateById();
    }
}
